/*
 * dynarray.c
 * 
 * "pointterigeneerinen" dynaamisen kokoinen taulukko. Taulukkoon voi lisätä
 * minkä tahansa tyyppisiä pointer arvoja. Arvoja haettaessa palautetaan
 * void* joka täytyy castata oikean tyyppiseksi
 * 
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#include "dynarray.h"
#include "jsonparser.h"
#include "hashtable.h"

#define INIT_SIZE 10 /* Taulukon koko alussa */

/*
 *	Etsii alkion järjesteystä taulukosta binäärihaulla
 */
int da_binsearch(dynarray * array, void* find, int left, int right, int (*cmprf)(void*, void*)) { //Joni
    if (right < left) {
        return -1;
    }
    int mid = right + (left - right) / 2;
    if (cmprf(array->array[mid], find) > 0) {
        return da_binsearch(array, find, left, mid - 1, cmprf);
    } else if (cmprf(array->array[mid], find) < 0) {
        return da_binsearch(array, find, mid + 1, right, cmprf);
    } else {
        return mid;
    }

}

/*
 *	Etsii taulukosta paikan uudelle alkiolle
 */
int da_binsearch_inorder_index(dynarray * array, void* find, int left, int right, int (*cmprf)(void*, void*)) { //Joni
    if (right < left) {
        return left;
    }
    int mid = right + (left - right) / 2;
    if (cmprf(array->array[mid], find) > 0) {
        return da_binsearch_inorder_index(array, find, left, mid - 1, cmprf);
    } else if (cmprf(array->array[mid], find) < 0) {
        return da_binsearch_inorder_index(array, find, mid + 1, right, cmprf);
    } else {
        return mid;
    }

}


/*
 * Luo DynArrayn
 */
dynarray * da_new() { //Ossi
    dynarray * array = malloc(sizeof (dynarray));
    if (array != NULL) {
        int len = INIT_SIZE;

        array->array = malloc(len*sizeof (void*));
        if (array->array == NULL) {
            free(array);
            return NULL;
        } else {
            array->max_len = len;
            array->top = 0;
        }
    }
    for (unsigned int i = 0; i < array->max_len; ++i) {
        array->array[i] = NULL;
    }
    return array;
}

/* 
 * Lisää DynArrayn loppuun dataa
 */
bool da_append(dynarray * array, void * data) { //Joni
    return da_append_in(array,data,array->top);
}

/*
 *	Lisää DynArrayn kohtaan index dataa
 */
bool da_append_in(dynarray * array, void * data, unsigned int index) { //Joni
    if (array == NULL) {
        return false;
    }

    if (array->max_len == array->top) {
        array->max_len = array->max_len * 2;
        void* tmp = realloc(array->array, sizeof (void*) * array->max_len);
        if (tmp == NULL) {
            return false;
        }
        array->array = (void**) tmp;
    }

    for (unsigned int i = array->top; i > index; --i) {
        array->array[i] = array->array[i - 1];
    }

    da_set(array, index, data);
    array->top += 1;
    return true;
}

/*
 *	Lisää järjestettyyn DynArrayyn dataa oikeaan kohtaan
 */
int da_append_inorder(dynarray * array, void * data, int (*cmprf)(void*, void*), bool append_if_exists) { //Joni
    unsigned int new_index = da_binsearch_inorder_index(array, data, 0, array->top - 1, cmprf);
	if (new_index<array->top && cmprf(da_get(array,new_index),data) == 0) {
		if (append_if_exists && da_append_in(array, data, new_index)) {
			return new_index;
		}
		return new_index;
	}
	else { 
		if (da_append_in(array, data, new_index)) {
		   return new_index;
		}
	}
    return -1;
}

/*
 * Palauttaa DynArrayn arvon kohdasta index
 */
void* da_get(dynarray * array, unsigned int index) { //Joni
    void* data = NULL;

    if (index < array->top) {
        data = array->array[index];
    }

    return data;
}

/*
 * Asettaa arvon DynArrayyn kohtaan index
 */
bool da_set(dynarray * array, unsigned int index, void * data) { //Joni

    array->array[index] = data;
    return true;
}

/*
 * Poistaa arvon arrayn kohdasta index
 */
void* da_remove(dynarray* array, unsigned int index) { //Joni
    void* data = array->array[index];
    array->array[index] = NULL;

    for (unsigned int i = index + 1; i < array->top; ++i) {
        array->array[i - 1] = da_get(array, i);
    }


    array->top--;
    return data;
}

/*
 * Tuhoaa DynArrayn
 */
void da_free(dynarray * array) { //Joni
    free(array->array);
    free(array);
}

/**
 * Vapauttaa rekursiivisesti dynarray<json_item> tyyppisen taulukon
 */
void da_free_json(dynarray* da){ //Ossi
    
    for (unsigned int i = 0; i < da->top; ++i){
        json_item* tmp = (json_item*)da_get(da,i);
        if (tmp != NULL){
            json_item_free(tmp);
        }
    }
    da_free(da);
    
}
